package api

import (
	"context"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/ovh/cds/engine/api/group"
	"github.com/ovh/cds/engine/api/services"
	"github.com/ovh/cds/engine/api/workermodel"
	"github.com/ovh/cds/engine/service"
	"github.com/ovh/cds/sdk"
)

func (api *API) putBookWorkerModelHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		if ok, err := isHatchery(ctx); err != nil {
			return err
		} else if !ok {
			return sdk.WithStack(sdk.ErrForbidden)
		}

		vars := mux.Vars(r)

		groupName := vars["permGroupName"]
		modelName := vars["permModelName"]
		g, err := group.LoadByName(ctx, api.mustDB(), groupName)
		if err != nil {
			return err
		}

		m, err := workermodel.LoadByNameAndGroupID(ctx, api.mustDB(), modelName, g.ID, workermodel.LoadOptions.Default)
		if err != nil {
			return err
		}

		s, err := services.LoadByID(ctx, api.mustDB(), getUserConsumer(ctx).AuthConsumerUser.Service.ID)
		if err != nil {
			return err
		}

		if err := workermodel.BookForRegister(api.Cache, m.ID, s.ID); err != nil {
			return err
		}

		return nil
	}
}

func (api *API) putSpawnErrorWorkerModelHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		var spawnErrorForm sdk.SpawnErrorForm
		if err := service.UnmarshalBody(r, &spawnErrorForm); err != nil {
			return sdk.WrapError(err, "Unable to parse spawn error form")
		}

		vars := mux.Vars(r)

		groupName := vars["permGroupName"]
		modelName := vars["permModelName"]
		g, err := group.LoadByName(ctx, api.mustDB(), groupName)
		if err != nil {
			return err
		}

		model, err := workermodel.LoadByNameAndGroupID(ctx, api.mustDB(), modelName, g.ID, workermodel.LoadOptions.Default)
		if err != nil {
			return err
		}

		tx, err := api.mustDB().Begin()
		if err != nil {
			return sdk.WithStack(err)
		}
		defer tx.Rollback() // nolint

		if spawnErrorForm.Error == "" && len(spawnErrorForm.Logs) == 0 {
			return nil
		}

		if err := workermodel.UpdateSpawnErrorWorkerModel(tx, model.ID, spawnErrorForm); err != nil {
			return sdk.WrapError(err, "cannot update spawn error on worker model")
		}

		if err := tx.Commit(); err != nil {
			return err
		}

		workermodel.UnbookForRegister(ctx, api.Cache, model.ID)

		return service.WriteJSON(w, nil, http.StatusOK)
	}
}

func (api *API) getWorkerModelsEnabledHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// this handler should only answer to an hatchery
		hatchery, err := isHatchery(ctx)
		if err != nil {
			return err
		}
		if !hatchery && !isMaintainer(ctx) {
			return sdk.WithStack(sdk.ErrForbidden)
		}

		consumer := getUserConsumer(ctx)
		var groupIDs []int64
		if len(consumer.AuthConsumerUser.GroupIDs) > 0 {
			groupIDs = consumer.AuthConsumerUser.GroupIDs
		} else {
			groupIDs = append(getUserConsumer(ctx).GetGroupIDs(), group.SharedInfraGroup.ID)
		}

		// for an hatchery the token was generated by a admin for all consumer's groups.
		models, err := workermodel.LoadAllUsableByGroupIDs(context.TODO(), api.mustDB(), groupIDs, workermodel.LoadOptions.Default)
		if err != nil {
			return err
		}

		return service.WriteJSON(w, models, http.StatusOK)
	}
}

func (api *API) getWorkerModelSecretHandler() service.Handler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
		// this handler should only answer to an hatchery
		hatchery, err := isHatchery(ctx)
		if err != nil {
			return err
		}
		if !hatchery && !isMaintainer(ctx) {
			return sdk.WithStack(sdk.ErrForbidden)
		}

		vars := mux.Vars(r)

		groupName := vars["permGroupName"]
		modelName := vars["permModelName"]

		g, err := group.LoadByName(ctx, api.mustDB(), groupName)
		if err != nil {
			return err
		}

		m, err := workermodel.LoadByNameAndGroupID(ctx, api.mustDB(), modelName, g.ID)
		if err != nil {
			return err
		}

		secrets, err := workermodel.LoadSecretsByModelID(ctx, api.mustDB(), m.ID)
		if err != nil {
			return err
		}

		return service.WriteJSON(w, secrets, http.StatusOK)
	}
}
